//Copyright 2012 Google Inc. All Rights Reserved.

/**
* "GA VIZ" JAVASCRIPT LIBRARY v4.3.1
*
* @authors
*	Shan Aminzadeh, shan.aminzadeh@gmail.com
*	Arya Bondarian, aryabond@gmail.com
*	Albert Gau, dirtbagzfan@gmail.com
*	Travis Lai, travisrlai@gmail.com
*	Daniel Nguyen, danielnuwin@gmail.com
* 
* @fileoverview
*	This library is designed to help translate Google Analytics API queries
*	into Google Visualizations seamlessly. 
* 
* @license
*	This work is free software; you can redistribute it and/or modify it under
*	the terms of the GNU General Public License as published by the Free 
*	Software Foundation.
*
* @userdoc
*	For help using this document, visit:
*		http://qrgen.site90.com/google/user-doc.html
*/

/**
* Refer to the Client ID and API Key given to the user when they enable their
* Google Analytics Account with the API. Necessary to verify the user has
* access to the Google Analytics profile.
*/
var clientId, apiKey;

/**
* Refers to the API scope that the user will need authentication for.
*/
var scopes = 'https://www.googleapis.com/auth/analytics.readonly';

/**
* List of Chart Objects that will hold all of the visualizations if they can't
* be rendered yet.
*/
var listOfCharts = [];

/**
* A boolean that tells us when the client has finished loading.
*/
var clientLoaded = false;

/**
* Starting function that should check authentication.
*/
handleClientLoad = function () {
	// Load the Visualization API and the piechart package.
	gapi.client.setApiKey(apiKey);
	window.setTimeout(gaViz.checkAuth,1);
}

/**
* Namespace of the library. Named after the authors of this library.
*/
var gaViz = {};

/**
* A function for setting the API Key and Client ID.
* @param (obj) settings - S
*/
gaViz.configAccount = function (settings) {
	apiKey = settings.apiKey;
	clientId = settings.clientId;
}

google.load('visualization', '1', {'packages':['corechart','table']});

/**
* Does an automated check to see if the user is authenticated.
*/
gaViz.checkAuth = function () {
	gapi.auth.authorize(
		{client_id: clientId, scope: scopes, immediate: true},
		gaViz.handleAuthResult);
}

/**
* 
* @param (authResult) object - The result of the authentication attempt. If the
* 	auth passes, then the library loads the charts. If it the authResult doesn't
*	pass, then a button is enabled that allows the user to authenticate.
*/
gaViz.handleAuthResult = function (authResult) {
	var authorizeButton = document.getElementById('authorize-button');
	if (authResult) {
		authorizeButton.style.visibility = 'hidden';

		// Load Google Analytics
		gapi.client.load('analytics', 'v3', function() {

			clientLoaded = true;
			// Render each chart in the array list
			for(var i = 0; i < listOfCharts.length; i++){
				listOfCharts[i].render();
			}
		});

	} else {
		authorizeButton.style.visibility = '';
		authorizeButton.onclick = gaViz.handleAuthClick;
	}
}
/**
* An authentication for when the user clicks a button. The button should prompt
* for authorization credentials.
* @event
*/
gaViz.handleAuthClick = function () {
	gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false},
		gaViz.handleAuthResult);
	return false;
}

/**
* A Chart object is the primary object in the library.
* @param (object) config - Config is the object that defines 
* @Constructor
*/
gaViz.Chart = function (config){
	this.config = config; // Object that contains all parameters
   
	// if client is loaded, render the chart
	// else, add it to a queue of charts to render
	if(clientLoaded)
		this.render();
	else
		listOfCharts.push(this);
}

/**
* Puts the response into  data table.
* @param (object) response - A Google Analytics response
* @return (object) data - A Google Data Table filled with the response
*/
gaViz.Chart.prototype.getDataTable = function (resp) {
	var data = new google.visualization.DataTable();
	var numOfColumns = resp.columnHeaders.length;
	var numResults = resp.totalResults;

	for(var i = 0; i < numOfColumns; i++){
		var dataType = resp.columnHeaders[i].dataType;
		var name = resp.columnHeaders[i].name;

		if(name == "ga:date" &&
			!(this.config.type == "column" || this.config.type == "bar")){
			dataType = 'date';
		} else if(dataType == "STRING"){
			dataType = 'string';
		} else if(dataType == "INTEGER"){
			dataType = 'number';
		}
		data.addColumn(dataType, this.formatMetricDim(name));
	}

	for(var i = 0; i < numResults; i++){
		var arrayMetrics = [];

		for(var j = 0; j < numOfColumns; j++ ){
			var name = resp.columnHeaders[j].name;
			var dataType = resp.columnHeaders[j].dataType;

			if(name == "ga:date" && 
				!(this.config.type == "column" || this.config.type == "bar")){
				arrayMetrics.push(this.stringToDate(resp.rows[i][j]));
			}else if(dataType == "INTEGER"){
				arrayMetrics.push(parseInt(resp.rows[i][j]));
			} else {
				arrayMetrics.push(resp.rows[i][j]);
			}
		}
		data.addRow(arrayMetrics);
	}
	return data;
}

/**
* Function that checks the library defined type and retrieves the corresponding
* Google Visualization. If no type is defined, then the visualization defaults
* to table.
* @return (object) - returns the corresponding Google Visualization
*/
gaViz.Chart.prototype.getChart = function () {

	this.validateParam();
	var div = document.getElementById(this.config.divContainer);
	
	if(this.config.type == 'line')
		return new google.visualization.LineChart(div);
	else if(this.config.type == 'bar')
		return new google.visualization.BarChart(div);
	else if(this.config.type == 'pie')
		return new google.visualization.PieChart(div);
	else if(this.config.type == 'column')
		return new google.visualization.ColumnChart(div);
	else
		return new google.visualization.Table(div);
}

/**
* Populates the chart with the data from the data table.
* @param (object) chart - The chart object you wish to draw the datat into.
* @param (object) dataTable - The Google data table holding the response.
*/
gaViz.Chart.prototype.draw = function (chart, dataTable) {
	chart.draw(dataTable, this.config.chartOptions);
}

/**
* Default callback for creating visualizations with a response.
* First the response is put into a data table, and the corresponding
* chart is retrieved. The two are then combined into a chart populated
* with the data.
* @param (object) resp - A Google Analytics API response
*/
gaViz.Chart.prototype.defaultCallback = function(resp) {

	if(resp.error){
		this.divError("RENDER ERROR: " + resp.error.code + 
		" " + resp.error.message);
	}

	var dataTable = this.getDataTable(resp); // converts response to data table
	var chart = this.getChart();  // returns a chart object based on this.config.chartType
	this.draw(chart, dataTable);  // simple draw method. Developers could write their own here.
}

/**
 * Binds a method to it's object.
 * @param {Object} object The main object to bind to.
 * @param {Object} method The method to bind to the object.
 * @return {Function} the function passed in boound to the object parameter.
 */
gaViz.Chart.prototype.bindMethod = function (object, method) {
	return function() {
		return method.apply(object, arguments);
	};
};

/**
* Executes the Google Analytics request and then executes the callback with
* the request.
*/
gaViz.Chart.prototype.render = function () {

	if(this.config.lastNtime){
		this.config.query['end-date'] = this.dateToString(new Date());
		this.config.query['start-date'] = this.setStartDate();
	}

	var request = gapi.client.analytics.data.ga.get(this.config.query);

	if(this.config.callback)
		request.execute(this.bindMethod(this, this.config.callback));
	else
		request.execute(this.bindMethod(this, this.defaultCallback));
}

/**
* Checks for config options specified by this lib. If there are errors
* then a error message is displayed.
*/
gaViz.Chart.prototype.validateParam = function () {

	if(!this.config.divContainer 
		|| document.getElementById(this.config.divContainer) == null) {
		var errMsg = 'MISSING CHART PARAMETER: "divContainer" is missing';
		if(this.config.chartOptions.title)
			errMsg += ' in ' + this.config.chartOptions.title ;
		alert(errMsg);
	}
}

/**
* Print an error message in the specified div. If the div does not exist, the
* message is printed in an alert.
* @param (string) message - The message you wish to display.
*/
gaViz.Chart.prototype.divError = function (message) {
	if(this.config.divContainer) {
		var div = document.getElementById(this.config.divContainer);
		var errorText = document.createTextNode(message + "\r\n");
		var font = document.createElement("font");
		font.style.color = "red";
		font.appendChild(errorText);
		div.appendChild(font);
	}
	else
		alert(message);
}

/**
* Formats the Google Metrics and Dimensions into readable strings
* @param (string) metric - returns a metric
*/
gaViz.Chart.prototype.formatMetricDim = function(metric) {
	var strLen = metric.length;
	var index;
	var strings = new Array();
	var upper = false;
	var finalString;

	// Checks for upper case letters, character by character
	for(var i = 0; i < strLen; i++) {
		var character = metric.charAt(i);
		if(character == character.toUpperCase() && character != ':'){
			index = i;
			upper = true;
		}
	}

	// If there is a upper case letter found, then split it into separate words
	// Else, trim the first three characters ("ga:")
	if(!upper) {
		finalString = this.trim(metric);
	}
	else {
		//var tmp = index;
		strings[0] = metric.substring(3,index);
		strings[1] = metric.substring(index);

		finalString = strings[0].charAt(0).toUpperCase() + strings[0].slice(1) + " " + strings[1];
	}

	return finalString;
}

/**
* Removes the "ga:" from the legend label and capitalizes the first letter
* @param (string) string = The string that needs trimming
* @return (string) capital - the formatted string.
*/
gaViz.Chart.prototype.trim = function(string) {
	var name = string.substring(3);
	var capital = name.charAt(0).toUpperCase() + name.slice(1);

	return capital;
}

/**
* Sets the dynamic start date when the metric will be displaying a nTime segment
* @return (String) dateToString(date) - The new start date that was set.
*/
gaViz.Chart.prototype.setStartDate = function() {

	var date = new Date();
	nTime = this.config.lastNtime;

	if(nTime[0] == 'ga:date' || nTime[0] == 'ga:day')
		date.setDate(date.getDate() - nTime[1]);
	else if(nTime[0] == 'ga:hour')
		date.setHours(date.getHours() - nTime[1]);
	else if(nTime[0] == 'ga:month')
		date.setMonth(date.getMonth() - nTime[1]);
	else if(nTime[0] == 'ga:year')
		date.setYear(date.getFullYear() - nTime[1]);
	else if(nTime[0] == 'ga:week')
		date.setDate(date.getDate() - (nTime[1] * 7));
	else {
		this.divError('Invalid lastNtime parameter. ' +
			'Should follow format [string:"ga:metric",int:"value"]');
	}
	return this.dateToString(date);
}

/**
* Converts a String "YYYY-MM-DD" to a Date Object
* @param (String) - The string representation of the date.
* @return (Date) date - A JS Date object.
*/
gaViz.Chart.prototype.stringToDate = function(date){
    var year = date.substring(0,4);
    var month = date.substring(4,6);
    var day =  date.substring(6,8);

    if(month < 10){
        month = month.substring(1,2);
    }

    month = month - 1;

    if(day < 10){
        day = day.substring(1,2);
    }

    var dateObj = new Date(year, month, day);
    return dateObj;
}

/**
* Converts a Date Object to a String "YYYY-MM-DD"
* @param (Date) date - A JS Date object.
* @return (String) - The string representation of the date.
*/
gaViz.Chart.prototype.dateToString = function(date) {
	var year = date.getFullYear();
	var month = date.getMonth() + 1;
	var day = date.getDate();

	if(month < 10)
		month = '0' + month;
	if(day < 10)
		day = '0' + day;

	return (year + '-' + month + '-' + day);
}

// end of file

















